4. 쿠버네티스 개요3
3.4.3. 데몬셋 (DaemonSet)
데몬셋이란?
데몬셋의 개념:
디플로이먼트는 기본적으로 파드 그룹을 특정 노드가 아니라 클러스터 전체에서 N개 가동한다는 형태로 관리. 하지만 노드의 로그 수집, 모니터링처럼 노드와 밀접한 관계가 있는 애플리케이션이라면 각 노드에 데몬 프로세스처럼 1대씩 배포하는 쪽이 적절함
- 데몬셋(DaemonSet): 클러스터의 모든(또는 일부) 노드에 Pod를 하나씩 실행하도록 보장하는 컨트롤러
- 데몬 프로세스(Daemon Process): 백그라운드에서 지속적으로 실행되는 서비스 프로세스. 시스템 시작 시 자동 실행
- 노드 에이전트(Node Agent): 각 노드에서 실행되며 노드 관련 작업(로그 수집, 모니터링 등)을 수행하는 프로그램
Deployment vs DaemonSet:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Deployment]
클러스터 전체에 N개 배포
↓
[Node1: Pod1, Pod2] [Node2: Pod3] [Node3: (없음)]
특징:
├─ 스케줄러가 최적 노드 선택
├─ 노드별 파드 개수 불균등 가능
└─ replicas로 총 개수 지정
vs
[DaemonSet]
각 노드마다 1개씩 배포
↓
[Node1: Pod1] [Node2: Pod2] [Node3: Pod3]
특징:
├─ 모든 노드에 파드 1개씩 자동 배치
├─ 노드 추가 시 자동으로 파드 생성
└─ replicas 설정 없음 (노드 수 = 파드 수)
데몬셋의 특징:
데몬셋은 각 노드마다 파드가 하나씩 실행된 상태를 유지하는 기능
DaemonSet 동작 방식:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[초기 상태]
Node1, Node2, Node3 존재
↓
[DaemonSet 배포]
각 노드에 자동으로 파드 1개씩 생성
├─ Node1 → Pod1
├─ Node2 → Pod2
└─ Node3 → Pod3
↓
[노드 추가]
Node4가 클러스터에 추가됨
↓
[자동 확장]
Node4 → Pod4 (자동 생성)
↓
[파드 삭제 시]
Node1의 Pod1 삭제됨
↓
[자동 복구]
Node1 → Pod1-new (자동 재생성)
데몬셋의 주요 사용 사례
시스템 레벨 애플리케이션:
DaemonSet 활용 예시:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[로그 수집]
├─ Fluentd
├─ Logstash
└─ Filebeat
이유: 모든 노드의 로그를 수집해야 함
[모니터링]
├─ Prometheus Node Exporter
├─ Datadog Agent
└─ Grafana Agent
이유: 각 노드의 메트릭 수집 필요
[네트워크]
├─ kube-proxy
├─ Calico
└─ Cilium
이유: 각 노드의 네트워크 설정 관리
[스토리지]
├─ Ceph
├─ GlusterFS
└─ Longhorn
이유: 분산 스토리지 노드 구성
[보안]
├─ Falco (침입 탐지)
├─ Tetragon (보안 관찰)
└─ SELinux Agent
이유: 모든 노드의 보안 모니터링
- Fluentd: 로그 수집 및 전달을 위한 오픈소스 도구. 다양한 소스에서 로그를 수집하여 목적지로 전송
- Prometheus Node Exporter: 노드(서버)의 하드웨어 및 OS 메트릭을 수집하여 Prometheus로 전송하는 에이전트
- Calico/Cilium: Kubernetes 네트워크 플러그인(CNI). 네트워크 정책 및 라우팅 관리
- Falco: 컨테이너 런타임의 보안 위협을 실시간으로 탐지하는 오픈소스 도구
데몬셋 예제
기본 데몬셋 매니페스트:
파일명: ubuntu-daemonset.yaml
apiVersion: apps/v1 # Kubernetes API 버전
kind: DaemonSet # 리소스 타입 - DaemonSet (각 노드마다 파드 1개씩 배포)
metadata:
name: ubuntu-daemonset # 데몬셋 이름
spec:
selector: # 어떤 파드를 관리할지 선택하는 조건
matchLabels:
app: ubuntu # "app=ubuntu" 레이블을 가진 파드 관리
template: # 파드 생성 시 사용할 템플릿
metadata:
labels:
app: ubuntu # 생성되는 파드에 부여할 레이블
spec: # 파드 스펙 정의
containers:
- name: ubuntu # 컨테이너 이름
image: ubuntu:22.04 # Ubuntu 22.04 LTS 이미지
command: ["sh"] # 실행할 명령어
args:
- -euc # 옵션: -e (에러 시 종료), -u (미정의 변수 에러), -c (문자열을 명령어로 실행)
- "sleep infinity" # 컨테이너를 무한 대기 상태로 유지 (종료 방지)
데몬셋 배포:
# 데몬셋 배포
kubectl apply -f ubuntu-daemonset.yaml
# 데몬셋 상태 확인
kubectl get daemonsets
# 파드 확인 (-o wide로 어느 노드에 배치되었는지 확인)
kubectl get pods -l=app=ubuntu -o wide
출력 예시:
NAME READY STATUS RESTARTS AGE NODE
ubuntu-daemonset-abc12 1/1 Running 0 10s node1
ubuntu-daemonset-def34 1/1 Running 0 10s node2
ubuntu-daemonset-ghi56 1/1 Running 0 10s node3
각 노드에 파드가 정확히 1개씩 배치된 것을 확인할 수 있음
데몬셋의 고급 기능
특정 노드에만 배포:
노드 셀렉터를 사용하여 특정 레이블을 가진 노드에만 데몬셋 파드 배포 가능
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ssd-monitor
spec:
selector:
matchLabels:
app: ssd-monitor
template:
metadata:
labels:
app: ssd-monitor
spec:
nodeSelector: # 특정 노드에만 배포
disk-type: ssd # "disk-type=ssd" 레이블을 가진 노드에만 배포
containers:
- name: monitor
image: monitor:latest
노드 셀렉터 동작:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
클러스터 노드:
├─ Node1 (labels: disk-type=ssd) → Pod 배포 ✓
├─ Node2 (labels: disk-type=hdd) → Pod 배포 ✗
├─ Node3 (labels: disk-type=ssd) → Pod 배포 ✓
└─ Node4 (레이블 없음) → Pod 배포 ✗
결과: SSD 노드에만 모니터링 파드 배포
- 노드 셀렉터(nodeSelector): Pod를 특정 레이블을 가진 노드에만 배치하도록 지정하는 간단한 방법
Toleration을 사용한 마스터 노드 배포:
기본적으로 마스터 노드(Control Plane)에는 파드가 배포되지 않음. Toleration을 사용하면 마스터 노드에도 배포 가능
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: network-plugin
spec:
selector:
matchLabels:
app: network
template:
metadata:
labels:
app: network
spec:
tolerations: # Taint를 무시하고 배포
- key: node-role.kubernetes.io/control-plane # 마스터 노드의 Taint 키
effect: NoSchedule # NoSchedule Taint 무시
operator: Exists # 키가 존재하기만 하면 허용
containers:
- name: network
image: calico/node:latest
Taint와 Toleration:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Taint (오염)]
노드에 제약을 걸어 특정 파드만 배치
│
예시: 마스터 노드
└─ node-role.kubernetes.io/control-plane:NoSchedule
(일반 파드 배치 금지)
↓
[Toleration (용인)]
파드가 Taint를 무시하고 배치될 수 있도록 허용
│
예시: 네트워크 플러그인
└─ tolerations:
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
↓
결과: 마스터 노드를 포함한 모든 노드에 배포
- Taint(테인트): 노드에 제약을 걸어 특정 조건을 만족하는 Pod만 배치되도록 하는 기능. "오염"이라는 의미
- Toleration(톨러레이션): Pod가 특정 Taint를 무시하고 해당 노드에 배치될 수 있도록 허용하는 설정. "용인"이라는 의미
- NoSchedule: 해당 Taint를 용인하지 않는 Pod는 이 노드에 스케줄링되지 않음을 의미하는 effect
롤링 업데이트
데몬셋도 롤링 업데이트 지원:
디플로이먼트처럼 데몬셋도 무중단 롤링 업데이트 가능
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-collector
spec:
updateStrategy: # 업데이트 전략 설정
type: RollingUpdate # 롤링 업데이트 방식
rollingUpdate:
maxUnavailable: 1 # 업데이트 중 최대 1개 파드만 사용 불가 (한 번에 1개씩 업데이트)
selector:
matchLabels:
app: fluentd
template:
metadata:
labels:
app: fluentd
spec:
containers:
- name: fluentd
image: fluent/fluentd:v1.16 # 이미지 버전
업데이트 프로세스:
DaemonSet 롤링 업데이트:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
초기 상태 (v1.15)
[Node1: Pod v1.15] [Node2: Pod v1.15] [Node3: Pod v1.15]
↓
업데이트 시작 (v1.15 → v1.16)
[Node1: Pod v1.15 삭제] [Node2: Pod v1.15] [Node3: Pod v1.15]
maxUnavailable: 1 (최대 1개 노드만 파드 없음)
↓
[Node1: Pod v1.16 생성] [Node2: Pod v1.15] [Node3: Pod v1.15]
↓
[Node1: Pod v1.16] [Node2: Pod v1.15 삭제] [Node3: Pod v1.15]
↓
[Node1: Pod v1.16] [Node2: Pod v1.16 생성] [Node3: Pod v1.15]
↓
최종 상태 (v1.16)
[Node1: Pod v1.16] [Node2: Pod v1.16] [Node3: Pod v1.16]
업데이트 명령어:
# 이미지 업데이트
kubectl set image daemonset/log-collector fluentd=fluent/fluentd:v1.16
# 롤아웃 상태 확인
kubectl rollout status daemonset/log-collector
# 롤아웃 히스토리 확인
kubectl rollout history daemonset/log-collector
# 이전 버전으로 롤백
kubectl rollout undo daemonset/log-collector
데몬셋 vs 디플로이먼트 비교
배포 방식 비교:
| 항목 | DaemonSet | Deployment |
|---|---|---|
| 배포 방식 | 각 노드마다 1개 | 클러스터 전체에 N개 |
| replicas | 설정 불가 (자동으로 노드 수) | 설정 필요 |
| 스케줄링 | 모든 노드에 강제 배치 | 스케줄러가 최적 노드 선택 |
| 노드 추가 시 | 자동으로 파드 생성 | 변화 없음 |
| 노드 제거 시 | 해당 파드 자동 삭제 | 다른 노드로 재배치 |
| 용도 | 시스템 레벨 서비스 | 애플리케이션 서비스 |
| 롤링 업데이트 | 지원 | 지원 |
사용 사례 비교:
배포 타입 선택 가이드:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[DaemonSet 사용]
질문: 이 애플리케이션이 모든 노드에서 실행되어야 하나요?
├─ Yes → DaemonSet
│
예시:
├─ 로그 수집 (모든 노드의 로그 필요)
├─ 모니터링 (모든 노드의 메트릭 필요)
├─ 네트워크 플러그인 (모든 노드의 네트워크 설정)
└─ 보안 에이전트 (모든 노드 보안 감시)
[Deployment 사용]
질문: 애플리케이션 인스턴스가 몇 개 필요한가요?
├─ N개 → Deployment
│
예시:
├─ 웹 서버 (3개의 인스턴스로 로드 밸런싱)
├─ API 서버 (5개의 복제본)
├─ 백그라운드 워커 (2개의 워커)
└─ 마이크로서비스 (각 서비스별 N개)
실습: 노드 정보 수집 데몬셋 예제
간단한 노드 모니터링 데몬셋:
각 노드의 시스템 정보를 수집하여 출력하는 간단한 데몬셋 예제. Elasticsearch 같은 외부 서비스 없이 독립적으로 동작함
파일명: node-monitor-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-monitor
labels:
app: node-monitor
spec:
selector:
matchLabels:
app: node-monitor
template:
metadata:
labels:
app: node-monitor
spec:
tolerations: # 마스터 노드에도 배포
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
operator: Exists
containers:
- name: monitor
image: busybox:1.36
command:
- sh
- -c
- |
echo "=== Node Monitor Started on $(hostname) ==="
while true; do
echo "--- $(date) ---"
echo "Hostname: $(hostname)"
echo "Uptime: $(uptime)"
echo "Memory: $(free -m 2>/dev/null || cat /proc/meminfo | head -3)"
echo "Disk: $(df -h / 2>/dev/null | tail -1)"
echo ""
sleep 30
done
resources:
limits:
memory: 64Mi
cpu: 50m
requests:
memory: 32Mi
cpu: 10m
배포 및 확인:
# 데몬셋 배포
kubectl apply -f node-monitor-daemonset.yaml
# 데몬셋 상태 확인
kubectl get daemonsets
# 각 노드별 파드 배치 확인
kubectl get pods -l app=node-monitor -o wide
# 특정 파드의 로그 확인 (노드 정보 출력됨)
kubectl logs -l app=node-monitor --tail=20
# 실시간 로그 확인
kubectl logs -l app=node-monitor -f
출력 예시:
=== Node Monitor Started on node-monitor-abc12 ===
--- Wed Jan 15 12:00:00 UTC 2026 ---
Hostname: node-monitor-abc12
Uptime: 12:00:00 up 3 days, 2:30, 0 users, load average: 0.15, 0.10, 0.05
Memory: total used free
Mem: 7982 4521 1203
Disk: /dev/sda1 50G 12G 35G 26% /
리소스 정리:
kubectl delete -f node-monitor-daemonset.yaml
(참고) 프로덕션 로그 수집 데몬셋 예제
실제 프로덕션 환경에서 Fluentd를 사용한 로그 수집 구성 예시. Elasticsearch 클러스터가 먼저 구성되어 있어야 동작함
# 주의: Elasticsearch가 먼저 배포되어 있어야 함
# 학습용으로만 참고하고, 실제 실행은 Elasticsearch 구성 후 진행
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: kube-system
labels:
app: fluentd-logging
spec:
selector:
matchLabels:
app: fluentd-logging
template:
metadata:
labels:
app: fluentd-logging
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1.16-debian-elasticsearch8-1
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch.logging.svc.cluster.local"
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: containers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log
- name: containers
hostPath:
path: /var/lib/docker/containers
Fluentd + Elasticsearch 구성:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Node 1] [Node 2] [Node 3]
│ │ │
[Fluentd Pod] [Fluentd Pod] [Fluentd Pod]
│ │ │
├─ /var/log 수집 ├─ /var/log 수집 ├─ /var/log 수집
└─ 컨테이너 로그 수집 └─ 컨테이너 로그 수집 └─ 컨테이너 로그 수집
│ │ │
└─────────────────────┼─────────────────────┘
↓
[Elasticsearch 클러스터]
↓
[Kibana UI]
↓
로그 검색 및 시각화
3.4.4. 잡 (Job)과 크론잡 (CronJob)
잡 (Job)이란?
배치 작업을 위한 리소스:
파드를 배치 작업처럼 단발적으로 실행하는 사례에 유용한 잡. 디플로이먼트나 데몬셋과 달리 잡은 파드를 지속적으로 실행하지 않고, 작업 완료 후 종료됨
- 잡(Job): 한 번 실행 후 완료되는 배치 작업을 관리하는 Kubernetes 컨트롤러
- 배치 작업(Batch Job): 한 번에 대량의 데이터를 처리하거나 특정 작업을 일괄 수행하는 프로그램
- 종료 상태 코드(Exit Code): 프로그램 종료 시 반환하는 숫자. 0은 성공, 0이 아닌 값은 실패를 의미
Deployment vs Job:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Deployment]
파드를 계속 실행 (무한 루프)
│
├─ 웹 서버: 요청 대기 중
├─ API 서버: 요청 처리 중
└─ 데이터베이스: 연결 대기 중
파드 종료 시: 자동 재시작 (항상 실행 상태 유지)
vs
[Job]
파드를 한 번 실행하고 종료
│
├─ 데이터 백업: 백업 완료 후 종료
├─ 데이터 마이그레이션: 마이그레이션 완료 후 종료
└─ 보고서 생성: 생성 완료 후 종료
파드 종료 시: 재시작 안 함 (완료 상태로 유지)
잡의 기능:
잡은 다음 항목을 설정할 수 있음:
Job 설정 옵션:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
성공 조건
├─ completions: 최소한 성공해야 하는 파드 개수
└─ parallelism: 동시 실행 파드 개수
실패 처리
├─ backoffLimit: 허용 가능 실패 횟수
└─ restartPolicy: 실패 시 재시작 정책 (Never, OnFailure)
타임아웃
└─ activeDeadlineSeconds: 최대 실행 시간
- completions: Job이 성공으로 완료되기 위해 성공해야 하는 Pod의 총 개수
- parallelism: 동시에 실행할 수 있는 Pod의 최대 개수
- backoffLimit: Job이 실패로 처리되기 전까지 허용되는 재시도 횟수
- restartPolicy: Pod 실패 시 재시작 정책. Job에서는 Never(재시작 안 함) 또는 OnFailure(실패 시만 재시작)만 허용
쿠버네티스는 이에 따라 파드를 배포하고 실행. 파드 종료 상태 코드를 보고 성공 여부를 판단해서 재실행하는 등의 처리
잡 예제
기본 잡 매니페스트:
파일명: ubuntu-job.yaml
apiVersion: batch/v1 # Batch API 버전 (Job, CronJob용)
kind: Job # 리소스 타입 - Job (단발성 작업 실행)
metadata:
name: job-example # 잡 이름
spec:
completions: 3 # 3개의 파드가 정상 종료하면 잡 성공 (순차 실행)
parallelism: 1 # 동시에 실행할 파드 개수 (1개씩 순차 실행)
backoffLimit: 4 # 최대 재시도 횟수 (4번 실패하면 잡 실패 처리)
template: # 파드 템플릿
spec:
restartPolicy: Never # 파드 실패 시 재시작 안 함 (Job에서는 Never 또는 OnFailure만 허용)
containers:
- name: test # 컨테이너 이름
image: ubuntu:22.04 # Ubuntu 이미지
command: ["sh"] # 실행할 명령어
args:
- -euc # 옵션
- "echo done $(hostname)" # 호스트명 출력 후 종료
잡 실행:
# 잡 배포
kubectl apply -f ubuntu-job.yaml
# 잡 상태 실시간 확인 (-w는 watch 모드)
kubectl get jobs job-example -w -o custom-columns=NAME:.metadata.name,SUCCEEDED:.status.succeeded,FAILED:.status.failed
출력 예시:
NAME SUCCEEDED FAILED
job-example 0 0
job-example 1 0
job-example 2 0
job-example 3 0
Job 실행 과정:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
초기 상태
completions: 3
parallelism: 1
↓
[Pod 1 실행]
echo done job-example-abc12
→ 성공 (exit code 0)
SUCCEEDED: 1/3
↓
[Pod 2 실행]
echo done job-example-def34
→ 성공 (exit code 0)
SUCCEEDED: 2/3
↓
[Pod 3 실행]
echo done job-example-ghi56
→ 성공 (exit code 0)
SUCCEEDED: 3/3
↓
[잡 완료]
모든 파드 성공적으로 종료
파드 확인:
# 잡이 생성한 파드 목록 확인
kubectl get pods -l batch.kubernetes.io/job-name=job-example
출력 예시:
NAME READY STATUS RESTARTS AGE
job-example-abc12 0/1 Completed 0 2m
job-example-def34 0/1 Completed 0 1m
job-example-ghi56 0/1 Completed 0 30s
파드 그룹이 종료되어도 잡은 삭제되지 않고 남음. 따라서 잡에 속한 각 파드 로그를 실행한 후에도 확인할 수 있음
로그 확인:
# 잡의 모든 파드 로그 확인
kubectl logs -l batch.kubernetes.io/job-name=job-example
출력 예시:
done job-example-abc12
done job-example-def34
done job-example-ghi56
잡 삭제:
# 잡과 관련 파드 모두 삭제
kubectl delete -f ubuntu-job.yaml
# 또는
kubectl delete job job-example
병렬 잡 (Parallel Job)
여러 파드를 동시에 실행:
파일명: parallel-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: parallel-job
spec:
completions: 10 # 총 10개의 파드가 성공해야 잡 완료
parallelism: 3 # 동시에 3개의 파드를 병렬 실행
backoffLimit: 5 # 최대 5번까지 재시도
template:
spec:
restartPolicy: OnFailure # 실패 시 파드 재시작 (Never 대신 사용 가능)
containers:
- name: worker
image: busybox:1.36
command:
- sh
- -c
- |
echo "Worker started: $(hostname)"
sleep $((RANDOM % 10 + 5)) # 5~15초 랜덤 대기
echo "Worker completed: $(hostname)"
병렬 잡 실행 과정:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
초기 상태
completions: 10 (총 10개 성공 필요)
parallelism: 3 (동시에 3개 실행)
↓
[1단계: 3개 병렬 실행]
Pod1 실행 중...
Pod2 실행 중...
Pod3 실행 중...
↓
[Pod2 완료]
Pod1 실행 중...
Pod3 실행 중...
Pod4 실행 시작 (즉시 보충)
SUCCEEDED: 1/10
↓
[Pod1, Pod3 완료]
Pod4 실행 중...
Pod5 실행 시작
Pod6 실행 시작
SUCCEEDED: 3/10
↓
[반복...]
↓
[최종 완료]
SUCCEEDED: 10/10
실행 및 모니터링:
# 병렬 잡 배포
kubectl apply -f parallel-job.yaml
# 실시간 모니터링 (Active는 현재 실행 중인 파드 수)
kubectl get jobs parallel-job -w
# 파드 상태 확인
kubectl get pods -l batch.kubernetes.io/job-name=parallel-job -w
잡 실패 처리
실패 시나리오와 처리:
파일명: failing-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: failing-job
spec:
completions: 3
backoffLimit: 2 # 최대 2번까지만 재시도
template:
spec:
restartPolicy: Never
containers:
- name: test
image: busybox:1.36
command:
- sh
- -c
- exit 1 # 항상 실패 (exit code 1)
실패 잡 동작 과정:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Pod 1 실행]
exit 1 → 실패
FAILED: 1
↓
[Pod 2 실행] (재시도 1)
exit 1 → 실패
FAILED: 2
↓
[Pod 3 실행] (재시도 2)
exit 1 → 실패
FAILED: 3 (backoffLimit 초과)
↓
[잡 실패]
STATUS: Failed
더 이상 재시도 안 함
배포 및 확인:
# 실패 잡 배포
kubectl apply -f failing-job.yaml
# 잡 상태 실시간 확인
kubectl get jobs failing-job -w
# 파드 상태 확인 (Error 상태 확인)
kubectl get pods -l batch.kubernetes.io/job-name=failing-job
# 잡 상세 정보 (실패 원인 확인)
kubectl describe job failing-job
출력 예시:
NAME STATUS COMPLETIONS DURATION AGE
failing-job Failed 0/3 15s 15s
NAME READY STATUS RESTARTS AGE
failing-job-abc12 0/1 Error 0 15s
failing-job-def34 0/1 Error 0 10s
failing-job-ghi56 0/1 Error 0 5s
리소스 정리:
kubectl delete -f failing-job.yaml
타임아웃 설정
activeDeadlineSeconds 사용:
파일명: timeout-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: timeout-job
spec:
completions: 1
activeDeadlineSeconds: 60 # 60초 이내에 완료되어야 함
template:
spec:
restartPolicy: Never
containers:
- name: test
image: busybox:1.36
command:
- sh
- -c
- sleep 100 # 100초 대기 (60초 타임아웃으로 실패)
타임아웃 잡 동작 과정:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[잡 시작]
activeDeadlineSeconds: 60
↓
[Pod 실행]
sleep 100 (100초 대기 시작)
↓
[60초 경과]
타임아웃 발생
↓
[잡 강제 종료]
STATUS: Failed
reason: DeadlineExceeded
배포 및 확인:
# 타임아웃 잡 배포
kubectl apply -f timeout-job.yaml
# 잡 상태 실시간 확인 (60초 후 Failed로 변경됨)
kubectl get jobs timeout-job -w
# 파드 상태 확인
kubectl get pods -l batch.kubernetes.io/job-name=timeout-job
# 잡 상세 정보 (DeadlineExceeded 확인)
kubectl describe job timeout-job
출력 예시 (60초 후):
NAME STATUS COMPLETIONS DURATION AGE
timeout-job Failed 0/1 60s 60s
# describe 출력에서 확인 가능:
# Type Reason Message
# ---- ------ -------
# Warning DeadlineExceeded Job was active longer than specified deadline
리소스 정리:
kubectl delete -f timeout-job.yaml
크론잡 (CronJob)
크론잡이란?
주기적으로 실행되는 잡:
크론잡은 잡을 제어해서 크론 형식으로 시작 시간과 주기적 실행 방법을 설정하는 기능
- 크론잡(CronJob): Job을 지정된 스케줄에 따라 주기적으로 생성하는 Kubernetes 컨트롤러
- 크론(cron): Unix/Linux 시스템의 시간 기반 작업 스케줄러. 특정 시간에 명령을 실행
- 크론 표현식(Cron Expression): 작업 실행 시간을 정의하는 형식. "분 시 일 월 요일" 5개 필드로 구성
Job vs CronJob:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Job]
한 번만 실행
↓
kubectl apply -f job.yaml
↓
작업 실행 → 완료 → 종료
vs
[CronJob]
정해진 스케줄에 따라 반복 실행
↓
kubectl apply -f cronjob.yaml
↓
매시간 0분: 잡 생성 → 실행 → 완료
(반복)
크론 표현식:
크론 스케줄 형식:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* * * * *
│ │ │ │ │
│ │ │ │ └─ 요일 (0-6, 0=일요일)
│ │ │ └─── 월 (1-12)
│ │ └───── 일 (1-31)
│ └─────── 시 (0-23)
└───────── 분 (0-59)
예시:
0 * * * * # 매시간 0분
*/15 * * * * # 매 15분마다
0 2 * * * # 매일 오전 2시
0 0 * * 0 # 매주 일요일 자정
0 0 1 * * # 매월 1일 자정
30 3 * * 1-5 # 평일 오전 3시 30분
크론잡 예제
기본 크론잡 매니페스트:
파일명: backup-cronjob.yaml
apiVersion: batch/v1 # Kubernetes API 버전
kind: CronJob # 리소스 타입 - CronJob (주기적으로 잡 실행)
metadata:
name: backup-cronjob # 크론잡 이름
spec:
schedule: "0 2 * * *" # 크론 표현식: 매일 오전 2시에 실행
jobTemplate: # 생성할 잡의 템플릿
spec:
completions: 1 # 잡당 1개의 파드만 성공하면 됨
template: # 파드 템플릿
spec:
restartPolicy: OnFailure # 실패 시 재시작
containers:
- name: backup # 컨테이너 이름
image: ubuntu:22.04 # 이미지
command:
- sh
- -c
- |
echo "Backup started at $(date)"
echo "Backing up database..."
sleep 10
echo "Backup completed at $(date)"
크론잡 배포:
# 크론잡 배포
kubectl apply -f backup-cronjob.yaml
# 크론잡 목록 확인
kubectl get cronjobs
# 크론잡 상세 정보
kubectl describe cronjob backup-cronjob
출력 예시:
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
backup-cronjob 0 2 * * * False 0 <none> 10s
크론잡 고급 설정
히스토리 관리:
apiVersion: batch/v1
kind: CronJob
metadata:
name: cleanup-cronjob
spec:
schedule: "*/5 * * * *" # 5분마다 실행
successfulJobsHistoryLimit: 3 # 성공한 잡 3개만 보관
failedJobsHistoryLimit: 1 # 실패한 잡 1개만 보관
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: cleanup
image: busybox:1.36
command:
- sh
- -c
- echo "Cleanup completed"
히스토리 관리:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
시간 경과:
05:00 → Job1 성공
05:05 → Job2 성공
05:10 → Job3 성공
05:15 → Job4 성공 (Job1 자동 삭제, 최근 3개만 유지)
05:20 → Job5 성공 (Job2 자동 삭제)
보관된 잡: Job3, Job4, Job5
삭제된 잡: Job1, Job2
동시 실행 정책:
apiVersion: batch/v1
kind: CronJob
metadata:
name: long-running-cronjob
spec:
schedule: "* * * * *" # 매분 실행
concurrencyPolicy: Forbid # 동시 실행 금지
# Allow: 동시 실행 허용 (기본값)
# Forbid: 이전 잡이 실행 중이면 새 잡 건너뛰기
# Replace: 이전 잡 종료하고 새 잡 실행
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: worker
image: busybox:1.36
command:
- sh
- -c
- sleep 120 # 2분 대기 (다음 스케줄과 겹침)
동시 실행 정책 비교:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Allow - 동시 실행 허용]
10:00 → Job1 시작 (2분 소요)
10:01 → Job2 시작 (Job1 실행 중)
10:02 → Job1 완료, Job3 시작 (Job2 실행 중)
[Forbid - 동시 실행 금지]
10:00 → Job1 시작 (2분 소요)
10:01 → 건너뜀 (Job1 실행 중)
10:02 → Job1 완료, Job2 시작
[Replace - 기존 잡 교체]
10:00 → Job1 시작 (2분 소요)
10:01 → Job1 종료, Job2 시작
10:02 → Job2 종료, Job3 시작
- concurrencyPolicy: CronJob의 동시 실행 정책. Allow(허용), Forbid(금지), Replace(교체) 중 선택
- successfulJobsHistoryLimit: 성공한 Job 히스토리를 몇 개까지 보관할지 지정
- failedJobsHistoryLimit: 실패한 Job 히스토리를 몇 개까지 보관할지 지정
- startingDeadlineSeconds: 스케줄 시간 이후 Job을 시작해야 하는 마감 시간(초)
시작 기한 설정:
apiVersion: batch/v1
kind: CronJob
metadata:
name: strict-schedule-cronjob
spec:
schedule: "0 3 * * *" # 매일 오전 3시
startingDeadlineSeconds: 300 # 5분 이내에 시작 못 하면 건너뛰기
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: task
image: busybox:1.36
command: ["sh", "-c", "echo Task completed"]
시작 기한 동작:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
스케줄: 03:00
startingDeadlineSeconds: 300 (5분)
시나리오 1: 정상 시작
03:00 → 스케줄 시간
03:00:10 → 잡 시작 (10초 지연, OK)
시나리오 2: 지연 시작
03:00 → 스케줄 시간
03:04:30 → 잡 시작 (4분 30초 지연, OK)
시나리오 3: 마감 초과
03:00 → 스케줄 시간
03:06:00 → 마감 초과 (6분 지연, 건너뛰기)
크론잡 수동 실행
크론잡에서 즉시 잡 생성:
# 크론잡에서 수동으로 잡 생성
kubectl create job --from=cronjob/backup-cronjob manual-backup
# 생성된 잡 확인
kubectl get jobs
# 잡 로그 확인
kubectl logs job/manual-backup
실전 예제: 데이터베이스 백업 크론잡 (참고용, 기반 리소스 없이 실행하면 에러)
MySQL 백업 크론잡:
파일명: mysql-backup-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: mysql-backup
namespace: production
spec:
schedule: "0 1 * * *" # 매일 오전 1시
successfulJobsHistoryLimit: 7 # 일주일치 보관
failedJobsHistoryLimit: 3 # 실패 3개 보관
concurrencyPolicy: Forbid # 동시 백업 금지
jobTemplate:
spec:
backoffLimit: 3 # 3번 재시도
template:
spec:
restartPolicy: OnFailure
containers:
- name: mysql-backup
image: mysql:8.0
env:
- name: MYSQL_HOST
value: "mysql.production.svc.cluster.local"
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: username
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
command:
- sh
- -c
- |
BACKUP_FILE="/backup/mysql-$(date +%Y%m%d-%H%M%S).sql"
echo "Starting backup to $BACKUP_FILE"
mysqldump -h $MYSQL_HOST -u $MYSQL_USER -p$MYSQL_PASSWORD --all-databases > $BACKUP_FILE
echo "Backup completed: $(ls -lh $BACKUP_FILE)"
volumeMounts:
- name: backup-storage
mountPath: /backup
volumes:
- name: backup-storage
persistentVolumeClaim:
claimName: backup-pvc
크론잡 vs 외부 크론
쿠버네티스 크론잡 vs 리눅스 cron:
| 항목 | 쿠버네티스 CronJob | 리눅스 cron |
|---|---|---|
| 선언적 관리 | YAML로 버전 관리 가능 | crontab 파일 (수동 관리) |
| 고가용성 | 컨트롤 플레인이 자동 관리 | 단일 서버 (SPOF) |
| 리소스 제한 | 메모리/CPU 제한 가능 | 제한 없음 (시스템 리소스) |
| 로그 관리 | kubectl logs로 쉽게 확인 | syslog 또는 별도 설정 필요 |
| 확장성 | 여러 노드에서 실행 가능 | 단일 서버에서만 실행 |
| 모니터링 | Kubernetes 메트릭 통합 | 별도 모니터링 필요 |
참고 자료
공식 문서:
- DaemonSet: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/
- Job: https://kubernetes.io/docs/concepts/workloads/controllers/job/
- CronJob: https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/
- 크론 표현식: https://crontab.guru/
베스트 프랙티스:
- 데몬셋은 리소스 제한 필수 설정 (모든 노드에서 실행되므로)
- 잡은 항상
restartPolicy: Never또는OnFailure사용 - 크론잡은
successfulJobsHistoryLimit와failedJobsHistoryLimit설정 권장 - 장시간 실행되는 크론잡은
activeDeadlineSeconds설정 권장